package com.magenta.maxoptra.gis.format;

import com.magenta.maxoptra.gis.GeoCountry;
import com.magenta.maxoptra.gis.PostalCode;
import com.magenta.maxoptra.gis.format.postal.ParsePostalCodeException;
import com.magenta.maxoptra.gis.format.postal.PostalCodeFormatUK;

import java.util.HashMap;
import java.util.Map;

/**
 * @author Sergey Grachev
 */
public final class PostalCodeFormatter {
    /**
     * Use strict format rules
     */
    public static final short STRICT = 1;

    private static final Map<GeoCountry, PostalCodeFormat> FORMAT_MAP = new HashMap<GeoCountry, PostalCodeFormat>();

    static {
        FORMAT_MAP.put(GeoCountry.UK, new PostalCodeFormatUK());
    }

    private PostalCodeFormatter() {
    }

    /**
     * Replace default formatter of postal code
     *
     * @param country of postal code
     * @param format  new formatter for postal code of country
     * @throws IllegalArgumentException if <code>format</code> or <code>country</code> is null
     */
    public static void overrideFormat(final GeoCountry country, final PostalCodeFormat format) {
        if (format == null) {
            throw new IllegalArgumentException("New format value is null");
        }

        if (country == null) {
            throw new IllegalArgumentException("Country value is null");
        }

        FORMAT_MAP.put(country, format);
    }

    /**
     * See {@link PostalCodeFormatter#parse(com.magenta.maxoptra.gis.GeoCountry, String, int)}
     */
    public static PostalCode parse(final GeoCountry country, final String postalCode) throws ParsePostalCodeException {
        return parse(country, postalCode, 0);
    }

    /**
     * Parse postal code
     *
     * @param country    parse using rule of this country
     * @param postalCode postal code string
     * @param flags      dditional options for parsing, ex. {@link PostalCodeFormatter#STRICT}
     * @return new instance of postal code
     * @throws ParsePostalCodeException
     *                                  if has incorrect format of postal code
     * @throws IllegalArgumentException if <code>country</code> is null or not found formatter for country
     * @throws ClassCastException       if T is incorrect type of parsed postal code
     */
    public static PostalCode parse(final GeoCountry country, final String postalCode, final int flags) throws ParsePostalCodeException {
        if (country == null) {
            throw new IllegalArgumentException("Country value is null");
        }

        final PostalCodeFormat format = FORMAT_MAP.get(country);
        if (format == null) {
            throw new IllegalArgumentException("Not found format for country " + country);
        }

        //noinspection unchecked
        return format.parse(postalCode, flags);
    }

    /**
     * Convert postal code to  string
     *
     * @param postalCode parsed postal code
     * @return formatted string
     * @throws IllegalArgumentException if <code>country</code> is null or not found formatter for country
     */
    public static String format(final PostalCode postalCode) {
        if (postalCode == null) {
            throw new IllegalArgumentException("Postal code value is null");
        }

        final PostalCodeFormat format = FORMAT_MAP.get(postalCode.getCountry());
        if (format == null) {
            throw new IllegalArgumentException("Not found format for country " + postalCode.getCountry());
        }

        return format.format(postalCode);
    }
}

